﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Erau
{
    /// <summary>
    /// 상속되지 않은 EraVar 자체는 null의 의미로 사용되도록 만들어보자.
    /// </summary>
    [Serializable]
	public abstract class EraVar
	{
		public static implicit operator EraVar(int v) { return new EraInt(v); }
		public static implicit operator EraVar(string v) { return new EraString(v); }
		public static implicit operator EraVar(double v) { return new EraDouble(v); }

		public EraVar this[string key]
		{
			get
			{
				if (this is EraTable)
					return ((EraTable)this)[key];
				else
				{
					// 로그 남김.

					return new EraString("NOT_TABLE");
				}
			}
			set
			{
				if (this is EraTable)
					((EraTable)this)[key] = value;
				else
				{
					// 로그 남김.
				}
			}
		}
/*
        /// XML 직렬화를 구현하고 싶었지만 도저히 안되서 걍 일단은 바이너리 직렬화로 퉁침.
        /// 언젠가 여유가 되면 XML 직렬화 형식으로 바꿔보자. NEED_REFACTORING
		public XmlSchema GetSchema() { return (null); }
		virtual public void WriteXml(XmlWriter writer) { }
		virtual public void ReadXml(XmlReader reader) { }
*/
	}

	[Serializable]
	public class EraInt : EraVar
	{
		public int Value;

		public EraInt() { Value = 0; }
		public EraInt(int v) { Value = v; }
/*
		public static EraIntVar operator +(EraIntVar v1, EraIntVar v2)
		{
			return new EraIntVar(v1.Value + v2.Value);
		}
*/
		// int형과의 암시적 형변환을 지원.
		public static implicit operator int(EraInt v) { return v.Value; }
		public static implicit operator EraInt(int v) { return new EraInt(v); }

		// double->int 변환을 암시적으로 지원함. 이때는 값 손실을 전제하므로 경고 로그를 남긴다.
		public static implicit operator EraInt(double v)
		{
			// 로그 남기기.

			return new EraInt((int)v);
		}

		// string->int도 지원. double을 쓴 건 소수점값 섞인 문자열이 들어왔을 때 double로 변환한 후 소수점을 버리기 위해서임.
		// 그냥 int.TryParse()를 시도하면 실수형 문자열을 변환할 수 없어 0이 돌아옴.
		public static implicit operator EraInt(string v)
		{
			double result;
			if (!double.TryParse(v, out result)) // 실패시 result에는 0이 할당됨.
			{
				// 로그 남김.
				// Log.Warning("문자열 '"+ v + "'를 정수형으로 변환할 수 없습니다.");
			}

			return new EraInt((int)result);
		}

		public static bool operator true(EraInt v) { return v != 0; }
		public static bool operator false(EraInt v) { return v == 0; }

		public override string ToString() {	return Value.ToString(); }
/*
		// Xml Serialization Infrastructure 
		override public void WriteXml(XmlWriter writer)
		{
			writer.WriteValue(Value);
		}

		override public void ReadXml(XmlReader reader)
		{
			Value = reader.ReadElementContentAsInt();
		}
*/
	}

	[Serializable]
	public class EraDouble : EraVar
	{
		double Value;

		public EraDouble() { Value = 0; }
		public EraDouble(double v) { Value = v; }

		// double형과의 암시적 형변환을 지원.
		public static implicit operator double(EraDouble v) { return v.Value; }
		public static implicit operator EraDouble(double v) { return new EraDouble(v); }

		//  string->double도 지원.
		public static implicit operator EraDouble(string v)
		{
			double result;
			if (!double.TryParse(v, out result)) // 실패시 result에는 0이 할당됨.
			{
				// 로그 남김.
				// Log.Warning("문자열 '"+ v + "'를 정수형으로 변환할 수 없습니다.");
			}

			return new EraDouble(result);
		}

		public static bool operator true(EraDouble v) { return v != 0; }
		public static bool operator false(EraDouble v) { return v == 0; }

		public override string ToString() {	return Value.ToString(); }
/*
		// Xml Serialization Infrastructure 
		override public void WriteXml(XmlWriter writer)
		{
			writer.WriteValue(Value);
		}

		override public void ReadXml(XmlReader reader)
		{
			Value = reader.ReadElementContentAsDouble();
		}
*/
	}

	[Serializable]
	public class EraString : EraVar
	{
		public string Value;

		public EraString(string v) { Value = v; }
		public EraString() { Value = string.Empty; }

		// string형과의 암시적 형변환을 지원.
		public static implicit operator string(EraString v) { return v.Value.ToString(); }
		public static implicit operator EraString(string v) { return new EraString(v); }

		public static bool operator true(EraString v) { return v != string.Empty; }
		public static bool operator false(EraString v) { return v == string.Empty; }

		public override string ToString() {	return Value; }
/*
		// Xml Serialization Infrastructure 
		override public void WriteXml(XmlWriter writer)
		{
			writer.WriteValue(Value);
		}

		override public void ReadXml(XmlReader reader)
		{
			Value = reader.ReadElementContentAsString();
		}
*/
	}

	//[XmlRoot("GameData")]
    [Serializable]
	public class EraTable : EraVar
	{
		Dictionary<string, EraVar> table = null;
		public IDictionary<string, EraVar> Table { get { return table; } }

		public new EraVar this[string key]
		{
			get
			{
				// 요청이 들어올 때 초기화(lazy initialization)
				if (table == null) table = new Dictionary<string, EraVar>();

				if (table.ContainsKey(key))
				{
					return table[key];
				}
				else
				{
					// 로그 남김

					return new EraString();
				}
			}
			set
			{
				// 요청이 들어올 때 초기화(lazy initialization)
				if (table == null) table = new Dictionary<string, EraVar>();

				table[key] = value;
			}
		}

		public static implicit operator string(EraTable v) 
		{ 
			// 로그 남김.
			return "TABLE"; 
		}

		public static bool operator true(EraTable v) { return v.table != null; }
		public static bool operator false(EraTable v) { return v.table == null; }

        public void Clear()
        {
            if (table == null) return;

            foreach (KeyValuePair<string, EraVar> v in table)
            {
                if (v.Value is EraTable)
                    ((EraTable)v.Value).Clear();
            }

            table.Clear();
        }
/*
		// http://stackoverflow.com/questions/12554186/how-to-serialize-deserialize-to-dictionaryint-string-from-custom-xml-not-us
		#region IXmlSerializable Members
		public XmlSchema GetSchema() { return (null); }

		public void ReadXml(XmlReader reader)
		{
			XmlSerializer serializer = new XmlSerializer(typeof(XMLItem), new XmlRootAttribute() { ElementName = "items" });
			this.table = ((XMLItem[])serializer.Deserialize(reader)).ToDictionary(i => i.key, i => i.value);
		}

		public void WriteXml(XmlWriter writer)
		{
			if (table == null) return;

			XmlSerializer serializer = new XmlSerializer(typeof(XMLItem[]), new XmlRootAttribute() { ElementName = "EraTable" });
			serializer.Serialize(writer,
						  table.Select(kv => new XMLItem() { type = GetTypeNameForXML(kv.Value), key = kv.Key, value = kv.Value }).ToArray());
		}

		private string GetTypeNameForXML(EraVar v)
		{
			if (v is EraInt) return "Int";
			else if (v is EraDouble) return "Double";
			else if (v is EraString) return "String";
			else if (v is EraTable) return "Table";
			else return "UnKnown";
		}
		#endregion
*/
	}
/*
	//[XmlType("EraVar")]
	public struct XMLItem
	{
		[XmlAttribute]
		public string type;

		public string key;

		public EraVar value;
	}
*/
}
